/* ----------------------------------------------------------------------
 * File: 		lexer.cpp
 * Created by:		Taylor Wood
 * Email:		Tdwood2@asu.edu
 * Student ID:		1202553801
 *
 * Description:
 * This file contains sever function definition such as:
 * isOperator
 * isDelimeter
 * LEXER
 * 	as well as various other function definitions.
 * ----------------------------------------------------------------------*/

#include "lexer.h"

int wordptr = 0;
stateDef currState = STATE_0;		// initialize to state 0

/*
 * Function:		isOperator
 * Argument(s):		char
 * Return:		boolean
 * 
 * Description: This function determines whether a character is an operator
 * or not.
 */
bool isOperator(char item) {
	if (item == '+' || item == '-' || item == '/' || item == '<' || item == '>' || item == '%' || item == '*' || item == '=' || item == '&' || item == '!' || item == '|') 
		return true;
	return false;
}


/*
 * Function:		isDelimeter
 * Argument(s):		char
 * Return:		boolean
 * 
 * Description: This function determines whether a character is a delimeter
 * or not.
 */
bool isDelimiter(char item) {
	if (item == ';' || item == '(' || item == ')' || item == '[' || item == ']' || item == '{' || item == '}')
		return true;
	return false;	
}


/*
 * Function:		lexer
 * Argument(s):		string
 * Return:		string
 * 
 * Description: Determines what the word is. A word can be an operator,
 * delimiter, integer, float, hexadecimal, octal, string, char, ID,
 * and keyword. 
 */
std::string lexer(std::string word) {
	
	//traverse through the word changing states
	for (int i = 0; i < word.length(); i++) {
		wordptr = i;
		DFA(word[i]);
	}
		
	// lastly check and see if the word is a keyword
	if (currState == STATE_ID)
		isKeyword(word);

	return DFAStrRet();
}


/*
 * Function:		DFA
 * Argument(s):		char
 * Return:		void
 * 
 * Description: Serves as a DFA.
 */
void DFA(char item) {
	
	switch (currState) {		
		case STATE_0:	
			if (isDelimiter(item)) 
				currState = STATE_DELIMITER;
			else if (isOperator(item))	
				currState = STATE_OPERATOR;
			// check to see if an integer was recieved
			// i wish we could use regex
			else if (item == '0') 
				currState = STATE_DIGIT;
			else if (isdigit(item))
				currState = STATE_INTEGER;
			else if (item == '\'') 
				currState = STATE_TOCHAR;
			else if (item == '\"')
				currState = STATE_INSTR;
			else if (item == '_')
				currState = STATE_INID;
			else if (isalpha(item) || item == '$')
				currState = STATE_ID;
			else 
				currState = STATE_UNKNOWN;
			break;
		case STATE_DIGIT:
			if (item == '0' || item == '1' || item == '2' || item == '3' || item == '4' || item == '5' || item == '6' || item == '7')
				currState = STATE_OCTAL;
	        	else if (item == '0' || item == '1' || item == '2' || item == '3' || item == '4' || item == '5' || item == '6' || item == '7' || item == '8' || item == '9')
				currState = STATE_INTEGER;

			else if (item == '.') 
				currState = STATE_TOFLOAT;
			// x is always in the second spot 0x
			// if not, its not a valid hex
			else if (item == 'x' && wordptr == 1)
				currState = STATE_INHEX;
			else
				currState = STATE_UNKNOWN;	
			break;
		case STATE_OCTAL:
			if (item == '0' || item == '1' || item == '2' || item == '3' || item == '4' || item == '5' || item == '6' || item == '7')
				currState = STATE_OCTAL;
			else if (item == '8' || item == '9')
				currState = STATE_INTEGER;
			else	
				currState = STATE_UNKNOWN;
			break;
		case STATE_INTEGER:
			if (isdigit(item))
				currState = STATE_INTEGER;
			else if (item == '.')
				currState = STATE_TOFLOAT;
			else
				currState = STATE_UNKNOWN;
			break;
		case STATE_TOFLOAT:
			if (item == '0' || item == '1' || item == '2' || item == '3' || item == '4' || item == '5' || item == '6' || item == '7' || item == '8' || item == '9')
				currState = STATE_FLOAT;
			else
				currState = STATE_UNKNOWN;
			break;
		case STATE_FLOAT:
			if (item == '0' || item == '1' || item == '2' || item == '3' || item == '4' || item == '5' || item == '6' || item == '7' || item == '8' || item == '9') 
				currState = STATE_FLOAT;
			else
				currState = STATE_UNKNOWN;
			break;
		case STATE_TOCHAR:
			if (isalpha(item) || isDelimiter(item))
				currState = STATE_INCHAR;
			else if (item == '\\')
				currState = STATE_INCHARSA;
			else
				currState = STATE_UNKNOWN;
			break;
		case STATE_INCHAR:
			if (item == '\'') 
				currState = STATE_CHAR;
			else
				currState = STATE_UNKNOWN;
			break;
		case STATE_INCHARSA:
			if (item == '\'' || item == '\\' || isalpha(item) || item == '\"')
				currState = STATE_INCHAR;
			else
				currState = STATE_UNKNOWN;
			break;
		case STATE_INSTR:
			if (item == '\\')
				currState = STATE_INSTRSA;
			else if	(item == '\"')
				currState = STATE_STRING;
			else
				currState = STATE_INSTR;
			break;
		case STATE_INSTRSA:
			if (item == '\"' || item == '\\' || isalpha(item) || isDelimiter(item) || isdigit(item) || item == '\'') 
				currState = STATE_INSTR;
			else
				currState = STATE_UNKNOWN;
			break;
		case STATE_INHEX:
			if (isdigit(item) || item == 'a' || item == 'A' || item == 'b' || item == 'B' || item == 'c' || item == 'C' || item == 'd' || item == 'D' || item == 'e' || item == 'E' || item == 'f' || item == 'F')
				currState = STATE_HEX;
			else
				currState = STATE_UNKNOWN;
		case STATE_HEX:
			if (isdigit(item) || item == 'a' || item == 'A' || item == 'b' || item == 'B' || item == 'c' || item == 'C' || item == 'd' || item == 'D' || item == 'e' || item == 'E' || item == 'f' || item == 'F')
				currState = STATE_HEX;
			else
				currState = STATE_UNKNOWN;
			break;
		case STATE_INID:
			if (isdigit(item) || isalpha(item))
				currState = STATE_ID;
			else if (item == '_')
				currState = STATE_INID;
			else
				currState = STATE_UNKNOWN;
			break;
		case STATE_ID:
			if (isdigit(item) || isalpha(item) || item == '_' || item == '-')
				currState = STATE_ID;
			else	
				currState = STATE_UNKNOWN;
			break;			
		default:
			currState = STATE_UNKNOWN;
			break;
	}
	
}



/*
 * Function:		DFAStrRet
 * Argument(s):		void
 * Return:		string
 * 
 * Description: Determines what string to return based on the state.
 */
std::string DFAStrRet(void) {
	
	// the string to return
	std::string retString = "";

	switch (currState) {
		
		case STATE_DELIMITER:
			retString = "DELIMITER";
			break;
		case STATE_OPERATOR:
			retString = "OPERATOR";
			break;
		case STATE_INTEGER:
			// integer needs an extra tab since its a short string
			retString = "INTEGER\t";
			break;
		case STATE_DIGIT:
			// integer needs an extra tab since its a short string
			retString = "INTEGER\t";
			break;
		case STATE_FLOAT:
			retString = "FLOAT\t";
			break;
		case STATE_CHAR:
			retString = "CHAR\t";
			break;
		case STATE_STRING:
			retString = "STRING\t";
			break;
		case STATE_HEX:
			retString = "HEXADECIMAL";
			break;
		case STATE_OCTAL:
			retString = "OCTAL\t";
			break;
		case STATE_ID:
			retString = "ID\t";
			break;
		case STATE_KEYWORD:
			retString = "KEYWORD\t";
			break;
		default:
			retString = "UNDEFINED";
			break;
	}
	
	// return to initial state
	currState = STATE_0;
	
	return retString;
}


/*
 * Function:		isKeyword
 * Argument(s):		string
 * Return:		void
 * 
 * Description: Determines if an id matches a keywork or not.
 */
void isKeyword(std::string word) {
		if (!word.compare("if"))
			currState = STATE_KEYWORD;
		else if (!word.compare("else"))
			currState = STATE_KEYWORD;
		else if (!word.compare("while"))
			currState = STATE_KEYWORD;
		else if (!word.compare("return"))
			currState = STATE_KEYWORD;
		else if (!word.compare("int"))
			currState = STATE_KEYWORD;
		else if (!word.compare("float"))
			currState = STATE_KEYWORD;
		else if (!word.compare("void"))
			currState = STATE_KEYWORD;
		else if (!word.compare("char"))
			currState = STATE_KEYWORD;
		else if (!word.compare("string"))
			currState = STATE_KEYWORD;
		else if (!word.compare("boolean"))
			currState = STATE_KEYWORD;
}
